package com.thingcom.delinrobot.Common.Frame;

import android.util.Log;

import androidx.annotation.Nullable;

import com.thingcom.delinrobot.Common.Constant;
import com.thingcom.delinrobot.Common.pjo.FrameType;
import com.thingcom.delinrobot.utils.HexStrUtils;
import com.thingcom.delinrobot.utils.HexUtil;
import com.thingcom.delinrobot.utils.MyLog;

import java.nio.charset.StandardCharsets;

import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_MAIN_HOME;
import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_MAIN_INFO;
import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_MAIN_START;
import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_MAIN_STOP;
import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_ROBOT_CURRENT_TIME;

import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_ROBOT_PIN_CHANGE;
import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_ROBOT_PIN_QUERY;
import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_ROBOT_WORK_AREA_READ;
import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_ROBOT_WORK_AREA_SET;
import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_ROBOT_WORK_TIME_READ;
import static com.thingcom.delinrobot.Common.pjo.FrameType.TYPE_FRAME_ROBOT_WORK_TIME_SET;

/**
 * author : YuFeng
 * e-mail : 923462992@qq.com
 * date   : 2020/1/2
 *
 * 帧解析工具，一条帧的格式为：
 * 帧头：  0x68 --------1字节
 * 控制码： C   --------1字节
 * MAC地址：A0 A1 A2 A3 ----4字节
 * 帧计数器： F0 -------1字节
 * 数据域长度：L -------1字节
 * 数据域 ：DATA -------n字节
 * 校验码： CS   -------1字节
 * 帧尾：  0x16  -------1字节
 *
 */

public class FrameUtils {
    private static final String TAG = "FrameUtils";

    //帧特征
    public static final int INDEX_OF_CONTROL_CODE = 1; //控制码所在位置
    public static final int INDEX_OF_DATA_TYPE = 5; //帧类型所在位置
    public static final int INDEX_OF_COUNT = 6;//帧计数器所在位置

    public static final int INDEX_OF_CS_FROM_END = 1; //校验位所在的位置（从尾部算起）
    public static final int INDEX_OF_MAC = 2;//一条帧MAC所在位置
    public static final int INDEX_OF_IDENTIFY = 8;//帧标识所在的位置
    public static final int INDEX_OF_DATA_COUNT = 7;

    public static final int TEMPERATURES_PER_FRAME_SIZE_MAX = 126;//每条帧最大携带的温度组数

    public static int sFrameNum = (int) (Math.random()*255);//帧序号，0-255


    public static int getCurrentFrameNum() {
        sFrameNum++;
        if (sFrameNum == 255) {
            sFrameNum = 0;
        }
        return sFrameNum;
    }

    /**
     * 给帧设置控制码
     * @param oneFrame
     * @param controlCode "01" "80"等
     */
    public static void setControlCode(byte[] oneFrame,String controlCode){
        byte code = HexUtil.hexStringToBytes(controlCode)[0];
        oneFrame[1] = code;
    }



    /**
     * 获取帧的命令标识，字符串8位 byte数组4位
     * @param oneFrame
     * @return
     */
    @Nullable
    public static String getDataIdentify(byte[] oneFrame) {
        MyLog.i("1234", "第一步getDataIdentify: "+getFrameString(INDEX_OF_IDENTIFY, 4, oneFrame));
        return getFrameString(INDEX_OF_IDENTIFY, 4, oneFrame);
    }

    /**
     * 发送event
     * @param identify
     */

    public static void sendEventByDataIdentify(FrameType identify,byte[] receiveFrame){
        byte[] dataArea = FrameUtils.getDataArea(receiveFrame);
        switch (identify) {
            case TYPE_FRAME_MAIN_INFO:
                String dataArea1 = HexStrUtils.bytesToHexString(dataArea);
                Log.i("1234", "sendEventByDataIdentify: " + "dataArea" + dataArea1);
                break;
            case TYPE_FRAME_MAIN_HOME:
                break;
            case TYPE_FRAME_MAIN_STOP:
                break;
            case TYPE_FRAME_ROBOT_CURRENT_TIME:
                break;
            case TYPE_FRAME_MAIN_START:
                break;
            case TYPE_FRAME_ROBOT_PIN_CHANGE:
                break;
            case TYPE_FRAME_ROBOT_PIN_QUERY:
                break;
            case TYPE_FRAME_ROBOT_WORK_AREA_READ:
                break;
            case TYPE_FRAME_ROBOT_WORK_AREA_SET:
                break;
            case TYPE_FRAME_ROBOT_WORK_TIME_READ:
                break;
            case TYPE_FRAME_ROBOT_WORK_TIME_SET:
                break;
            default:
                break;
        }
    }

    /**
     * 获取帧的命令标识，枚举
     *
     * @param oneFrame
     * @return
     */
    public static FrameType getDataIdentifyEnum(byte[] oneFrame) {
        String identify = getDataIdentify(oneFrame);
        MyLog.i("1234", "identify: " + identify);
        if (identify == null) return null;
        if (identify.equals(TYPE_FRAME_MAIN_INFO.getIdentify())) {
            return TYPE_FRAME_MAIN_INFO;
        }
        if (identify.equals(TYPE_FRAME_MAIN_HOME.getIdentify())) {
            return TYPE_FRAME_MAIN_HOME;
        }
        if (identify.equals(TYPE_FRAME_MAIN_STOP.getIdentify())) {
            return TYPE_FRAME_MAIN_STOP;
        }
        if (identify.equals(TYPE_FRAME_ROBOT_CURRENT_TIME.getIdentify())) {
            return TYPE_FRAME_ROBOT_CURRENT_TIME;
        }
        if (identify.equals(TYPE_FRAME_MAIN_START.getIdentify())) {
            return TYPE_FRAME_MAIN_START;
        }
//        if (identify.equals(TYPE_FRAME_ROBOT_LANGUAGE_SET.getIdentify())) {
//            return TYPE_FRAME_ROBOT_LANGUAGE_SET;
//        }
        if (identify.equals(TYPE_FRAME_ROBOT_PIN_CHANGE.getIdentify())) {
            return TYPE_FRAME_ROBOT_PIN_CHANGE;
        }
        if (identify.equals(TYPE_FRAME_ROBOT_PIN_QUERY.getIdentify())) {
            return TYPE_FRAME_ROBOT_PIN_QUERY;
        }
        if (identify.equals(TYPE_FRAME_ROBOT_WORK_AREA_READ.getIdentify())) {
            return TYPE_FRAME_ROBOT_WORK_AREA_READ;
        }
        if (identify.equals(TYPE_FRAME_ROBOT_WORK_AREA_SET.getIdentify())) {
            return TYPE_FRAME_ROBOT_WORK_AREA_SET;
        }
        if (identify.equals(TYPE_FRAME_ROBOT_WORK_TIME_READ.getIdentify()))
            return TYPE_FRAME_ROBOT_WORK_TIME_READ;
        if (identify.equals(TYPE_FRAME_ROBOT_WORK_TIME_SET.getIdentify()))
            return TYPE_FRAME_ROBOT_WORK_TIME_SET;
//        if (identify.equals(TYPE_FRAME_SWITCH_STATE_GET.getIdentify())) {
//            return TYPE_FRAME_SWITCH_STATE_GET;
//        }
//        if (identify.equals(TYPE_FRAME_SWITCH_STATE_SET.getIdentify())) {
//            return TYPE_FRAME_SWITCH_STATE_SET;
//        }
        return null;
    }

    /**
     * 获取mac的镜像
     *
     * @param mirrorMac
     * @return
     */
    public static String getMacMirror(String mirrorMac) {
        byte[] ccMirrorByte = HexUtil.hexStringToBytes(mirrorMac);
        if (ccMirrorByte == null) return null;
        //交换
        for (int i = 0; i < 4 / 2; i++) {
            byte b = ccMirrorByte[i];
            ccMirrorByte[i] = ccMirrorByte[3 - i];
            ccMirrorByte[3 - i] = b;
        }
        return HexUtil.formatHexString(ccMirrorByte);
    }

    /**
     * 获取一条帧的MAC
     *
     * @param oneFrame
     * @param mirror   是否反转
     * @return
     */
    public static String getMac(byte[] oneFrame, boolean mirror) {
        if (!mirror) {
            return getFrameString(INDEX_OF_MAC, 4, oneFrame);
        } else
            return getMacMirror(getFrameString(INDEX_OF_MAC, 4, oneFrame));
    }


    /**
     * 给帧设置MAC
     *
     * @param oneFrame 帧
     * @param mac      mac  {@link #INDEX_OF_MAC}
     * @param mirror   是否需要镜像翻转
     */
    public static void setMAC(byte[] oneFrame, String mac, boolean mirror) {
        if (mac.length() != 8) {
            throw new RuntimeException("mac的格式不对");
        }
        byte[] macByte = HexUtil.hexStringToBytes(mac);
        if (mirror) {
            //mac要倒着写进入 01 04 00 01  写入后是 01 00 04 01
            for (int i = 0; i < 4; i++) {
                oneFrame[i + 2] = macByte[3 - i];
            }
        } else {
            for (int i = 0; i < 4; i++) {
                oneFrame[i + 2] = macByte[i];
            }
        }
        FrameUtils.setCS(oneFrame);
    }

    /**
     * 给帧设置控制码
     *
     * @param oneFrame 帧
     * @param identify {@link #INDEX_OF_IDENTIFY}
     */
    public static void setIdentify(byte[] oneFrame, String identify) {
        if (identify.length() != 8) {
            throw new RuntimeException("控制吗的格式不对");
        }
        byte[] macByte = HexUtil.hexStringToBytes(identify);
        for (int i = 0; i < 4; i++) {
            oneFrame[i + INDEX_OF_IDENTIFY] = macByte[i];
        }
        FrameUtils.setCS(oneFrame);
    }


    /**
     * 以字符串形式获取帧中的某一段
     *
     * @param index byte数组的起始位置
     * @param size  byte数组的长度
     * @return
     */
    @Nullable
    private static String getFrameString(int index, int size, byte[] oneFrame) {
        if (!checkCS(oneFrame)) {
            return null;
        }
        String one = HexUtil.formatHexString(oneFrame);
        return one.substring(index * 2, index * 2 + size * 2);
    }




    /**
     * 获取设备的开关状态
     *
     * @param oneFrame
     * @return true 开，false 关
     */
    public static boolean getDeviceState(byte[] oneFrame) {
        if (!checkCS(oneFrame)) {
            return false;
        }
        byte[] dataArea = getDataArea(oneFrame);
        if (dataArea == null || dataArea.length < 1) return false;
        return dataArea[0] == 0x01;
    }

    /**
     * 获取设备异常情况
     * @param oneFrame
     * @return true 异常
     */
    public static boolean getDeviceAbnormal(byte[] oneFrame){
        if (!checkCS(oneFrame)) {
            return false;
        }
        byte[] dataArea = getDataArea(oneFrame);
        if (dataArea == null || dataArea.length < 1) return false;
        return dataArea[0] == 0x02;
    }


    /**
     * 移除帧中的某一字节
     *
     * @param oneFrame
     * @param index
     * @return
     */
    public static byte[] removeElement(byte[] oneFrame, int index) {
        int resultLen = 0;
        byte[] newFrame = new byte[oneFrame.length -1];
        for (int i = 0; i < oneFrame.length-1; i++,resultLen++) {
            if (index == i) {
                resultLen = i+1;
            }
            newFrame[i] = oneFrame[resultLen];
        }

        newFrame[INDEX_OF_COUNT + 1]--;
        setCS(newFrame);
        return newFrame;
    }

    /**
     * 获取主动上报的设备开关状态  数据域还包括他的MAC有5个字节
     *
     * @param oneFrame
     * @return
     */
    public static boolean getCallbackState(byte[] oneFrame) {
        if (!checkCS(oneFrame)) {
            return false;
        }
        byte[] dataArea = getDataArea(oneFrame);
        if (dataArea == null || dataArea.length != 5) return false;
        return dataArea[4] == 0x01;
    }



   
    /**
     * 提取数据域，不包括命令标志
     *
     * @param oneFrame
     * @return
     */
    public static byte[] getDataArea(byte[] oneFrame) {
        if (!checkCS(oneFrame)) return null;
        int dataAndTailLength = getDateAndTailLength(oneFrame);
        byte[] dataArea = new byte[dataAndTailLength - 6];//不包括命令标识位4 bytes和尾部2 bytes
        if (dataAndTailLength == 6) { //数据域只有命令标志
            return null;
        }
        System.arraycopy(oneFrame, INDEX_OF_IDENTIFY + 4, dataArea, 0, dataArea.length);
        return dataArea;
    }

    /**
     * 检查校验值是否正确
     *
     * @param oneFrame
     * @return
     */
    private static boolean checkCS(byte[] oneFrame) {

        byte cs = getCs(oneFrame);
        MyLog.i("1234", "checkCS: "+cs+"对比：" + oneFrame[oneFrame.length - 1 - INDEX_OF_CS_FROM_END]);
        return cs == oneFrame[oneFrame.length - 1 - INDEX_OF_CS_FROM_END];
    }

    /**
     * 获得CS校验值（是一整条完整帧的校验值，包含了尾部，但是不会去计算尾部）
     *
     * @param oneFrame
     * @return
     */
    public static byte getCs(byte[] oneFrame) {

        int dataTailLength = getDateAndTailLength(oneFrame);
        int dataLength = dataTailLength - 2;
        MyLog.i("1234", "getCs: "+dataLength);
        byte[] frameForwardCs = new byte[dataLength + 8];//cs校验位以上
        System.arraycopy(oneFrame, 0, frameForwardCs, 0, frameForwardCs.length);
        return cs(frameForwardCs);
    }

    /**
     * 计算cs
     *
     * @param frame
     * @return
     */
    public static byte cs(byte[] frame) {
        int count = 0;
        for (byte per : frame) {
            count += per & 0xff;
        }
        Log.i("1234", "cs count :"+count);
        return (byte) (count & 0xff);
    }


    /**
     * 获得数据和尾部的长度和
     *
     * @param header
     * @return
     */
    public static int getDateAndTailLength(byte[] header) {
        String head = HexUtil.formatHexString(header);
        int lastIndex = Constant.FRAME_HEAD_LENGTH * 2;
        //  String lengthHighStr = head.substring(lastIndex-2,lastIndex);
        String lengthStr = head.substring(lastIndex - 2, lastIndex);

        //  int high = Integer.parseInt(lengthHighStr,16);
        int length = Integer.parseInt(lengthStr, 16);
        //  int total = high*256 + low;
        return length + 2;
    }


    /**
     * 检查是不是未知帧
     *
     * @param oneFrame
     * @return
     */
    public static boolean unKnowFrame(byte[] oneFrame) {
        if (isPulse(oneFrame)){
            return false;
        }
        if (!HexUtil.formatHexString(oneFrame).substring(0, 2).equals("69"))
            return true;
        if (!checkCS(oneFrame))
            return true;
        return false;
    }

    public static boolean isPulse(byte[] oneFrame){
        return HexUtil.formatHexString(oneFrame,true).equals("69 01 00 00 00 00 00 01 00 00 17");
        //69 01 00 00 00 00 00 01 00 00 17
    }


    /**
     * 给帧加序号
     *
     * @param oneFrame
     * @param count
     * @return
     */
    public static byte[] addFrameCount(byte[] oneFrame, int count) {
        byte countByte = (byte) (count & 0xff);
        oneFrame[INDEX_OF_COUNT] = countByte;
        setCS(oneFrame);
        return oneFrame;
    }

    /**
     * 获取帧序号
     *
     * @param oneFrame
     * @return
     */
    public static int getFrameCount(byte[] oneFrame) {
        if (!checkCS(oneFrame)) {
            return -1;
        }
        byte count = oneFrame[INDEX_OF_COUNT];
        return count > 0 ? count : count + 256;
    }

    /**
     * 给帧加校验位
     *
     * @param oneFrame
     * @return
     */
    public static void setCS(final byte[] oneFrame) {
        byte cs = getCs(oneFrame);
        oneFrame[oneFrame.length - 1 - INDEX_OF_CS_FROM_END] = cs;
    }

    /**
     * 给帧替换数据域，不包括控制码 ,使用utf-8编码
     * @param oneFrame
     * @param dataArea
     * @return
     */
    public static byte[] setDataArea(byte[] oneFrame ,String dataArea){
        byte[] dataB = dataArea.getBytes(StandardCharsets.UTF_8);
        return setDataArea(oneFrame,dataB);
    }

    /**
     * 给帧直接替换数据域，不包括控制码
     * @param oneFrame
     * @param dataArea
     * @return
     */
    public static byte[] setDataArea(byte[] oneFrame ,byte[] dataArea) {
        int lastDataSize = oneFrame[INDEX_OF_DATA_COUNT]; //包含了控制码的长度4
        //建立一个新的帧 +4是控制码
        byte[] newFrame = new byte[oneFrame.length - lastDataSize + dataArea.length + 4];
        //复制前段部分 ，包括控制码
        System.arraycopy(oneFrame, 0, newFrame, 0, INDEX_OF_DATA_COUNT + 1 + 4);
        //复制数据域,不包括控制码
        System.arraycopy(dataArea, 0, newFrame, INDEX_OF_DATA_COUNT + 1 + 4, dataArea.length);
        //复制后段部分
        System.arraycopy(oneFrame, INDEX_OF_DATA_COUNT + lastDataSize + 1, newFrame, INDEX_OF_DATA_COUNT + dataArea.length + 1 + 4, 2);
        //更改数据域长度
        newFrame[INDEX_OF_DATA_COUNT] = (byte) (dataArea.length + 4);
        //设置校验
        setCS(newFrame);
        return newFrame;
    }
}
